<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>Twisted Enterprise Row Objects</title>
</head>
<body>

<div class="note">
<p>
由于缺乏维护，<code>twisted.enterprise.row</code>
以及 <code>twisted.enterprise.reflector</code> 从Twisted8.0 版本
起开始不被受到推荐使用。
</p>

<p>
所以此文档的保留仅是为了方便那些还在使用现存旧版本代码的用户。
</p>
</div>


<h1>Twisted的企业级行对象（Twisted Enterprise Row Objects）</h1>

<p><code>twisted.enterprise.row</code> 模块是简单python对象与关系
数据库中行进行交流连接的一种方法。
它含有两个组件：<code>RowObject</code> 类，开发者可以通过继承它而
获得的子类来和每一个相关的关系表建立联系，
以及 <code>Reflector</code>类，它负责对数据库进行查询，插入，更新
删除等操作。</p>

<p>row组件（原文为row module）更适用于类似在线游戏、网站等类似需要
后台数据库接口的应用。它本身并非为python提供了一个完整的对象－
关系映射功能————它的长处是可以很好的处理一些比较容易就可以在关系数据
库中表示出来的数据结构。最合适使用它的方法是为一个已经存在的关系
数据库建立一个python的接口，而当反过来为一个已有的python系统添加
数据库的持久应用（原文为database persistance）时则显得有点不那么
合适了。
</p>

<p><em>如果说row对象并不适合你建立的系统模型，你最好自己直接使用
<a href="enterprise.xhtml">low-level database API</a> ， 
或者是在其之上建立自己的对象/关系中间层来解决问题。</em></p>

<h2>类定义（Class Definitions）</h2>

<p>为了和一个关系数据库表进行互交
程序员必须为每一张这样的表创建一个派生自<code>twisted.enterprise.row.
RowObject</code>的子类。这些子类必须定义一系列类属性，它们包含了
与自己相关的表的信息。这些类属性包括：
</p>

<ul>
  <li>列名（rowColumns） —— 表中列名和各列属性的列表，保持大小写的一致
  </li>
  <li>关键字列表(rowKeyColumns) —— 以该形式: <code>[(列名称,
  类型)]</code>记录的关键字的列表</li>
  <li>表明(rowTableName) —— 对应的表的名称</li>
</ul>

<p>此外还有两个可选的类变量可以被定义：</p>

<ul>
  <li>外键(rowForeignKeys) —— 以此形式：<code>[(表名, [(子列名, 子列类型), ...], 
   [(父列名, 父列类型), ...], 
   containerMethodName,autoLoad]</code>进行记录的对应到其他表的外键列表</li>
  <li>row的工厂方法(rowFactoryMethod) —— 创建自身(因该就是row.RowObject的子类
      )实例的方法 </li>
</ul>

<p>举个例子：</p>

<pre class="python">
class RoomRow(row.RowObject):
    rowColumns       = [("roomId",  "int"), 
                        ("town_id", "int"),
                        ("name",    "varchar"),
                        ("owner",   "varchar"),
                        ("posx",    "int"),
                        ("posy",    "int"),
                        ("width",   "int"),
                        ("height",  "int")]
    rowKeyColumns    = [("roomId",  "int4")]
    rowTableName     = "testrooms"
    rowFactoryMethod = [testRoomFactory]
</pre>

<p>当我们使用Reflector创建该类时，所有在列名列表（rowColumns）中
的各项将会成为该类的一个数据成员（data members）。
</p>

<h2>初始化（Initialization）</h2>

<p>在初始化的阶段，会建立一些SQL操作来和数据库进行互交。
它使用到了数据库的系统目录，同时需要的还有另外一些基本信息。
派生自RowClass的子类的类属性的作用便是提供这些信息。
这些子类在创建的时候会被传入一个Reflector进行处理。</p>

<p>现今在Twisted企业框架（Twisted Enterprise）中可用的reflectors：
一是SQL Reflector：用于关系数据库，使用的是python的数据库API
另外一个是XML Reflector：用于包含XML文件的文件系统，但是使用起来
速度非常缓慢。
</p>

<p>来看个例子，学习下如何通过SQLReflector来使用上面创建的RoomRow：</p>

<pre class="python">

from twisted.enterprise.sqlreflector import SQLReflector

dbpool = adbapi.ConnectionPool("pyPgSQL.PgSQL")
reflector = SQLReflector( dbpool, [RoomRow] )
</pre>

<h2>创建RowObject（Create RowObject）</h2>

<p>这里有两个方法用于创建一个RowObject——从数据库中取出已有的或者是
创建一个新实例并插入到数据库中。
</p>

<p>为了从数据库中取出行或者新加行，可以使用Reflector的loadObjectsFrom方法。
它的参数包括：表名，可选的“用户数据”参数，可选的“where子句”。当然了，当你
想要取出所有的行的时候，where子句是可以省略的。
例如：</p>

<pre class="python">
def gotRooms(rooms):
    for room in rooms:
        print "Got room:", room.id

d = reflector.loadObjectsFrom("testrooms", 
                              whereClause=[("id", reflector.EQUAL, 5)])
d.addCallback(gotRooms)
</pre>

<p>对于更高级的RowObject结构，loadObjectsFrom可以调用每个在RowClass子类
中被声明为类属性的工厂方法(也就是上文提到的factoyMethod)。对于每一个行对象，
我们都会调用这个工厂方法（参数是类对象，用户数据以及以列名作为关键字的一个字典，
其中存放了数据库中的数据）。这个工厂方法会返回一个完全组装好（fully populated）
的RowObject实例，
可以用来进行一些预处理，查询或者是数据格式的转换，以方便最终显示给用户。
一个factory方法的例子如下：
</p>

<pre class="python">
def testRoomFactory(roomClass, userData, kw):
    newRoom = roomClass(userData)
    newRoom.__dict__.update(kw)
    return newRoom</pre>

<p>我们还需要考虑如何往一个数据表中插入一个之前并不存在的实例。
在这种情况下，可以创建一个新的实例，指明它的主键，并未数据域进行赋值，
然后就可以将它穿递给Reflector的<code>insertRow</code>方法
来看个例子：</p>

<pre class="python">
    newRoom = RoomRow()
    newRoom.assignKeyAttr("roomI", 11)
    newRoom.town_id = 20
    newRoom.name = 'newRoom1'
    newRoom.owner = 'fred'
    newRoom.posx = 100
    newRoom.posy = 100
    newRoom.width = 15
    newRoom.height = 20
    reflector.insertRow(newRoom).addCallback(onInsert)
</pre>

<p>结果就是会在数据库的表中插入一个新行（内容由newRoom的属性决定）。
要注意<code>assignKeyAttr</code>这个方法使用来定义主键的值的——普通
的属性如果被这个函数进行赋值会抛出一个异常。这可以用来保护数据库
的一致性。
</p>


<h2>表与表之间的关系（Relationships Between Tables）</h2>

<p>当我们为一个RowClass指定一个外键的时候实际上已经创建了表于表之间的
某种联系。当 <code
class="python">loadObjectsFrom</code> 被调用的时候，它将自动
从指定的表中装载所有的相关行（children rows），这些行将会放到一个名字
叫作 <code>childRows</code> 的列表中，这个列表中会存放着这些rowObject，
如果我们又为这个外键关系定义了<em>containerMethod</em> 
(如果你忘记了这个函数，可以查看前面类定义一节)，那么这个函数将会被父行
（parent row）调用来处理自己的各个子行（children rows，前面翻译成了相关行）。
</p>

<p>在外键的定义中有一个属性 <em>autoLoad</em> ，它作为一个标志
来决定是否当父行（parent row）被加载的时候，所对应的子行（child row）
也会被自动加载。</p>

<h2>多次读取Row Objects（Duplicate Row Objects）（以前的翻译叫做复制行
，感觉两个都很别扭）</h2>

<p>如果一个reflector想要加载一个已经存在了的rowObject实例，它得到的
其实是已经存在的那个rowObject的一个引用，而并不会去新建一个。为了
完成这个功能，reflector有一个cache，里面保存着对所有已经加载的
rowObject的引用（原文为weak references），区分这些rowObjects的方法
是通过他们各自拥有一个独一无二的关键字值(keys)。
</p>

<h2>更新RowObject（Updating Row Objects）</h2>

<p>RowObjects 有一个属性<code>dirty</code> ，当该实例中任何一个
属性被修改以后，dirty将会被置为1。这个标志位的作用就是用来判断
RowObjects是有改动，从而判断是否有需要将更新后的值写回数据库。
此外，你可以重写 <code>setDirty</code> 方法实现一些更加复杂的逻辑。
</p>

<p>一旦通过dirty来确定我们需要更新数据库中的数据，可以把这个RowObject作为参数传给
Reflector的 <code>updateRow</code> 方法。例子：</p>

<pre class="python">
reflector.updateRow(room).addCallback(onUpdated)
</pre>

<p>为了应对频繁更新数据库的情况，reflector可以生成SQL语句用来更新，但是并不会立即
进行更新的操作。这在当我们有许许多多的更新操作要做的时候非常有用，
因为在最后可以将所有需要进行更新的操作一起通过一个请求完成提交。例子：
</p>

<pre class="python">
updateSQL = reflector.updateRowSQL(room)
</pre>

<h2>删除RowObject（Deleting Row Objects）</h2>

<p>为了删除一个RowObject实例，我们可以把它作为从参数传递给
Reflector 的 <code>deleteRow</code> 方法。
注意！删除Python中的RowObject并 <em>不会</em> 自动
删除其在数据库中对应的那一行。删除的一个简单例子：</p>

<pre class="python">
reflector.deleteRow(room)
</pre>

</body>
</html>
